// -----------------------------------------------------------------------------
//
// Copyright 2014(c) Analog Devices, Inc.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//  - Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//  - Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in
//    the documentation and/or other materials provided with the
//    distribution.
//  - Neither the name of Analog Devices, Inc. nor the names of its
//    contributors may be used to endorse or promote products derived
//    from this software without specific prior written permission.
//  - The use of this software may or may not infringe the patent rights
//    of one or more patent holders.  This license does not release you
//    from the requirement that you obtain separate licenses from these
//    patent holders to use this software.
//  - Use of the software either in source or binary form, must be run
//    on or directly connected to an Analog Devices Inc. component.
//
// THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// -----------------------------------------------------------------------------
// FILE NAME :  motor_driver.v
// MODULE NAME :motor_driver
// AUTHOR : acozma
// AUTHOR'S EMAIL : andrei.cozma@analog.com
//
// -----------------------------------------------------------------------------
`timescale 1ns / 1ps

//------------------------------------------------------------------------------
//----------- Module Declaration -----------------------------------------------
//------------------------------------------------------------------------------
module motor_driver
//----------- Parameters Declarations -------------------------------------------
#(
    parameter PWM_BITS = 11
)
//----------- Ports Declarations -----------------------------------------------
(
    input       clk_i,
    input       pwm_clk_i,
    input       rst_n_i,
    input       run_i,
    input       star_delta_i,	// 1 STAR, 0 DELTA
	input       dir_i,			// 1 CW, 0 CCW          
	input [2:0] position_i,
    input [PWM_BITS-1:0] pwm_duty_i,
    output      AH_o,
    output      BH_o,
    output      CH_o,
    output      AL_o,
    output      BL_o,
    output      CL_o
);

//------------------------------------------------------------------------------
//----------- Registers Declarations -------------------------------------------
//------------------------------------------------------------------------------
reg         		pwm_s;
reg [ 3:0]  		motor_state;
reg [15:0]  		align_counter;
reg [ 2:0] 			position_s;
reg [PWM_BITS-1:0] 	pwm_cnt;


//------------------------------------------------------------------------------
//----------- Wires Declarations -----------------------------------------------
//------------------------------------------------------------------------------
wire                align_complete;
wire [PWM_BITS-1:0] pwm_duty_s;
wire [1:0] 			commutation_table[0:2];

//------------------------------------------------------------------------------
//----------- Local Parameters -------------------------------------------------
//------------------------------------------------------------------------------
parameter OFF    = 3'b001;
parameter ALIGN  = 3'b010;
parameter RUN 	 = 3'b100;

parameter [PWM_BITS-1:0] ALIGN_PWM_DUTY = 2**(PWM_BITS-1) + 2**(PWM_BITS-4);
parameter [15:0] ALIGN_TIME = 16'h8000;

parameter [1:0] COMMUTATION_TABLE_DELTA_CW[0:5][0:2]  = {{ 2'd1, -2'd1, -2'd1}, {-2'd1,  2'd1, -2'd1}, { 2'd1,  2'd1, -2'd1}, {-2'd1, -2'd1,  2'd1}, { 2'd1, -2'd1,  2'd1}, {-2'd1,   2'd1,  2'd1}};
parameter [1:0] COMMUTATION_TABLE_DELTA_CCW[0:5][0:2] = {{-2'd1,  2'd1,  2'd1}, { 2'd1, -2'd1,  2'd1}, {-2'd1, -2'd1,  2'd1}, { 2'd1,  2'd1, -2'd1}, {-2'd1,  2'd1, -2'd1}, { 2'd1,  -2'd1, -2'd1}};

parameter [1:0] COMMUTATION_TABLE_STAR_CW[0:5][0:2]   = {{ 2'd1,  2'd0, -2'd1}, {-2'd1,  2'd1,  2'd0}, { 2'd0,  2'd1, -2'd1}, { 2'd0, -2'd1,  2'd1}, { 2'd1, -2'd1,  2'd0}, {-2'd1,   2'd0,  2'd1}};
parameter [1:0] COMMUTATION_TABLE_STAR_CCW[0:5][0:2]  = {{-2'd1,  2'd0,  2'd1}, { 2'd1, -2'd1,  2'd0}, { 2'd0, -2'd1,  2'd1}, { 2'd0,  2'd1, -2'd1}, {-2'd1,  2'd1,  2'd0}, { 2'd1,   2'd0, -2'd1}};

//------------------------------------------------------------------------------
//----------- Assign/Always Blocks ---------------------------------------------
//------------------------------------------------------------------------------
assign align_complete = align_counter < ALIGN_TIME ? 0 : 1;
assign pwm_duty_s  = motor_state == OFF ? 0 :
                     motor_state == ALIGN ? ALIGN_PWM_DUTY : pwm_duty_i;

assign commutation_table[0] = star_delta_i ? 
							  dir_i ? COMMUTATION_TABLE_STAR_CW[position_s][0]  : COMMUTATION_TABLE_STAR_CCW[position_s][0] :
							  dir_i ? COMMUTATION_TABLE_DELTA_CW[position_s][0] : COMMUTATION_TABLE_DELTA_CCW[position_s][0];
assign commutation_table[1] = star_delta_i ? 
							  dir_i ? COMMUTATION_TABLE_STAR_CW[position_s][1]  : COMMUTATION_TABLE_STAR_CCW[position_s][1] :
							  dir_i ? COMMUTATION_TABLE_DELTA_CW[position_s][1] : COMMUTATION_TABLE_DELTA_CCW[position_s][1];
assign commutation_table[2] = star_delta_i ? 
							  dir_i ? COMMUTATION_TABLE_STAR_CW[position_s][2]  : COMMUTATION_TABLE_STAR_CCW[position_s][2] :
							  dir_i ? COMMUTATION_TABLE_DELTA_CW[position_s][2] : COMMUTATION_TABLE_DELTA_CCW[position_s][2];
					 
//Motor Phases Control
assign AH_o = commutation_table[0] == 2'd1 ? ~pwm_s : commutation_table[0] == -2'd1 ?  pwm_s : 1;
assign AL_o = commutation_table[0] == 2'd1 ?  pwm_s : commutation_table[0] == -2'd1 ? ~pwm_s : 1;

assign BH_o = commutation_table[1] == 2'd1 ? ~pwm_s : commutation_table[1] == -2'd1 ?  pwm_s : 1;
assign BL_o = commutation_table[1] == 2'd1 ?  pwm_s : commutation_table[1] == -2'd1 ? ~pwm_s : 1;
			  
assign CH_o = commutation_table[2] == 2'd1 ? ~pwm_s : commutation_table[2] == -2'd1 ?  pwm_s : 1;
assign CL_o = commutation_table[2] == 2'd1 ?  pwm_s : commutation_table[2] == -2'd1 ? ~pwm_s : 1;
			  
			  
//Control the current motor state
always @(posedge clk_i)
begin
    if(rst_n_i == 1'b0)
    begin
        motor_state <= OFF;
		align_counter <= 0;
    end
    else
    begin
		case(motor_state)
			OFF:
			begin
				position_s  <= 0;
				motor_state <= (run_i == 1'b1 ? ALIGN : OFF);
			end
			ALIGN:
			begin
				position_s <= 0;
				if(align_complete == 1'b1)
				begin
					motor_state <= (run_i == 1'b1 ? RUN : OFF);
				end
				else
				begin
					motor_state <= (run_i == 1'b1 ? ALIGN : OFF);
				end
			end
			RUN:
			begin
				position_s  <= position_i - 1;
				motor_state <= (run_i == 1'b1 ? RUN : OFF);
			end        
			default:
			begin
				motor_state <= OFF;
			end
		endcase
		align_counter <= motor_state == ALIGN ? align_counter + 1 : 0;		
    end
end

//Generate the PWM signal
always @(posedge pwm_clk_i)
begin
    if((rst_n_i == 1'b0))
    begin
        pwm_cnt <= 0;
    end
    else
    begin
        pwm_cnt <= pwm_cnt < (2**PWM_BITS - 1) ? pwm_cnt + 1 : 0;
    end
    pwm_s <= pwm_cnt < pwm_duty_s ? 1 : 0;
end

endmodule
